<!-- uni_modules/zmt-avatar-cropper/components/zmt-avatar-cropper/zmt-avatar-cropper.vue -->
<template>
  <view class="cropper-wrapper" v-if="show">
    <view class="cropper-mask" @click="close"></view>
    <view class="cropper-container">
      <view class="cropper-header" @mousedown="startDrag">
        <text class="title"></text>
        <view class="close-btn" @click.stop="close">×</view>
      </view>
      
      <view class="cropper-content">
        <image 
          :src="imageSrc" 
          class="source-image" 
          mode="aspectFit"
          @load="imageLoaded"
          @error="imageError"
        />
        
		
		<!-- 添加遮罩层 -->
		      <view class="mask-overlay">
		        <view class="mask-top" :style="{ height: cropY + 'px' }"></view>
		        <view class="mask-bottom" :style="{ height: `calc(100% - ${cropY + cropSize}px)` }"></view>
		        <view class="mask-left" :style="{ width: cropX + 'px', top: cropY + 'px', height: cropSize + 'px' }"></view>
		        <view class="mask-right" :style="{ left: cropX + cropSize + 'px', width: `calc(100% - ${cropX + cropSize}px)`, top: cropY + 'px', height: cropSize + 'px' }"></view>
		      </view>
		
		      <view 
		        class="crop-box"
				@mousedown.stop="startCropMove"
		        :style="{ width: cropSize + 'px', height: cropSize + 'px', left: cropX + 'px', top: cropY + 'px' }"
		      >
			<!-- 九宫格虚线 -->
			        <view class="grid-line horizontal line1"></view>
			        <view class="grid-line horizontal line2"></view>
			        <view class="grid-line vertical line1"></view>
			        <view class="grid-line vertical line2"></view>
					
          <view class="resize-handle tr" @mousedown.stop="startResize('tr')"></view>
          <view class="resize-handle br" @mousedown.stop="startResize('br')"></view>
          <view class="resize-handle bl" @mousedown.stop="startResize('bl')"></view>
          <view class="resize-handle tl" @mousedown.stop="startResize('tl')"></view>
        </view>
      </view>

      <view class="preview-area">
        <view class="preview-title">预览</view>
        <canvas canvas-id="previewCanvas" class="preview-canvas"></canvas>
        <canvas canvas-id="exportCanvas" class="export-canvas"></canvas>
        <button class="confirm-btn" @click="confirmCrop">确定</button>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  props: {
    imageSrc: { //图片路径，临时路径
      type: String,
      required: true
    },
    isShow: { //是否显示
      type: Boolean,
      default: false
    },
	cropMinSize:{ //选取框最小尺寸
		type:Number,
		default:200
	},
	
	cropMaxSize:{ //选取框最大尺寸
		type:Number,
		default:450
	},
	cropDefaultSize:{ //选取框默认尺寸
		type:Number,
		default:400
	}
  },
  watch:{
	"isShow"(val){
		this.show = val;
	}
  },
  data() {
    return {
      cropX: 0,
      cropY: 0,
	  show:false,
      cropSize: 400,
      imageWidth: 0,
      imageHeight: 0,
      displayWidth: 0,
      displayHeight: 0,
      imageOffsetX: 0,
      imageOffsetY: 0,
      scale: 1,
      isDragging: false,
      isResizing: false,
      resizeDirection: '',
      startX: 0,
      startY: 0,
      initialSize: 400,
      initialX: 0,
      initialY: 0,
	  sourceX:0,
	  sourceY:0
    }
  },
  mounted() {
    this.initEvents()
  },
  beforeDestroy() {
    document.removeEventListener('mousemove', this.handleMouseMove)
    document.removeEventListener('mouseup', this.handleMouseUp)
  },
  methods: {

    initEvents() {
      document.addEventListener('mousemove', this.handleMouseMove)
      document.addEventListener('mouseup', this.handleMouseUp)
    },
    imageLoaded(e) {
      const { width, height } = e.detail
      const containerSize = 500
      
      this.scale = Math.min(containerSize / width, containerSize / height)
      this.displayWidth = width * this.scale
      this.displayHeight = height * this.scale
      
      this.imageOffsetX = (500 - this.displayWidth) / 2
      this.imageOffsetY = (500 - this.displayHeight) / 2
	  if(width > height && this.displayHeight <= this.cropDefaultSize){
		  this.cropSize = this.displayHeight;
	  }else if(height > width && this.displayWidth <=this.cropDefaultSize){
		  this.cropSize = this.displayWidth;
	  }else if(height == width && this.displayWidth <= this.cropDefaultSize){
		  this.cropSize = this.displayWidth;
	  }else if(height == width && width > this.cropSize){
		  this.cropSize = this.cropDefaultSize;
	  }
	  
      this.imageWidth = width
      this.imageHeight = height
      this.initCropPosition()
      this.updatePreview()
    },
    initCropPosition() {
      this.cropX = this.imageOffsetX + (this.displayWidth - this.cropSize) / 2
      this.cropY = this.imageOffsetY + (this.displayHeight - this.cropSize) / 2
    },
    startDrag(e) {
      this.isDragging = true
      this.startX = e.clientX - this.cropX
      this.startY = e.clientY - this.cropY
    },
    startCropMove(e) {
      this.isDragging = true
      this.startX = e.clientX - this.cropX
      this.startY = e.clientY - this.cropY
    },
    startResize(direction) {
      this.isResizing = true
      this.resizeDirection = direction
      this.initialSize = this.cropSize
      this.initialX = this.cropX
      this.initialY = this.cropY
      this.startX = event.clientX
      this.startY = event.clientY
    },
    handleMouseMove(e) {
      if (this.isDragging) {
        const newX = e.clientX - this.startX
        const newY = e.clientY - this.startY
        
        this.cropX = Math.max(this.imageOffsetX, 
          Math.min(this.imageOffsetX + this.displayWidth - this.cropSize, newX))
        this.cropY = Math.max(this.imageOffsetY, 
          Math.min(this.imageOffsetY + this.displayHeight - this.cropSize, newY))
        this.updatePreview()
      }
      
      if (this.isResizing) {
        const deltaX = e.clientX - this.startX
        const deltaY = e.clientY - this.startY
        const minSize = this.cropMinSize //选取框最小尺寸

        switch(this.resizeDirection) {
          case 'tr':
            const maxSizeTR = Math.min(
              this.imageOffsetY + this.displayHeight - this.cropY,
              this.cropMaxSize
            )
            this.cropSize = Math.min(maxSizeTR, Math.max(minSize, this.initialSize - deltaY))
            this.cropY = this.initialY + deltaY
            break
            
          case 'br':
            const delta = Math.max(deltaX, deltaY)
            const maxSizeBR = Math.min(
              this.imageOffsetX + this.displayWidth - this.cropX,
              this.imageOffsetY + this.displayHeight - this.cropY,
              this.cropMaxSize
            )
            this.cropSize = Math.min(maxSizeBR, Math.max(minSize, this.initialSize + delta))
            break
            
          case 'bl':
            const maxSizeBL = Math.min(
              this.imageOffsetX + this.displayWidth - this.cropX,
              this.cropMaxSize
            )
            this.cropSize = Math.min(maxSizeBL, Math.max(minSize, this.initialSize - deltaX))
            this.cropX = this.initialX + deltaX
            break
            
          case 'tl':
            const deltaTL = Math.max(-deltaX, -deltaY)
            const maxSizeTL = Math.min(
              this.imageOffsetX + this.displayWidth - (this.cropX - deltaX),
              this.imageOffsetY + this.displayHeight - (this.cropY - deltaY),
              this.cropMaxSize
            )
            this.cropSize = Math.min(maxSizeTL, Math.max(minSize, this.initialSize + deltaTL))
            this.cropX = this.initialX + deltaX
            this.cropY = this.initialY + deltaY
            break
        }

        this.cropX = Math.max(this.imageOffsetX, 
          Math.min(this.imageOffsetX + this.displayWidth - this.cropSize, this.cropX))
        this.cropY = Math.max(this.imageOffsetY, 
          Math.min(this.imageOffsetY + this.displayHeight - this.cropSize, this.cropY))
        this.updatePreview()
      }
    },
    handleMouseUp() {
      this.isDragging = false
      this.isResizing = false
    },
    updatePreview() {
          const ctx = uni.createCanvasContext('previewCanvas', this)
          // 计算实际裁剪区域（考虑图片偏移和缩放比例）
          const sourceX = Math.max(0, (this.cropX - this.imageOffsetX) / this.scale)
          const sourceY = Math.max(0, (this.cropY - this.imageOffsetY) / this.scale)
		  
		  this.sourceX = sourceX
		  this.sourceY = sourceY
          const sourceSize = Math.min(
            this.cropSize / this.scale,
            this.imageWidth - sourceX,
            this.imageHeight - sourceY
          )
    
          ctx.clearRect(0, 0, 100, 100)
          ctx.drawImage(
            this.imageSrc,
            sourceX,
            sourceY,
            sourceSize,
            sourceSize,
            0, 0, 100, 100
          )
          ctx.draw()
        },
    confirmCrop() {
		  var that = this;
		  uni.showLoading({
		  	title:'上传中···'
		  })
		  var sourceX = this.sourceX;
		  var sourceY = this.sourceY;
          const ctx = uni.createCanvasContext('previewCanvas', this)
          const sourceSize = Math.min(
            this.cropSize / this.scale,
            this.imageWidth - sourceX,
            this.imageHeight - sourceY
          )
          ctx.drawImage(
            this.imageSrc,
            sourceX,
            sourceY,
            sourceSize,
            sourceSize,
            0, 0, 100, 100
          )
          ctx.draw(false, () => {
            uni.canvasToTempFilePath({
              canvasId: 'previewCanvas',
              success: (res) => {
				that.uploadBase64ToCloudStorage(res.tempFilePath);
                this.close()
              },
              fail: (err) => {
                console.error('生成裁剪图片临时路径失败', err)
                this.$emit('error', '生成裁剪图片临时路径失败')
                this.close()
              }
            }, this)
          })
        },
    close() {
	  this.show = false;
      this.$emit('close', false)
    },
    imageError() {
      this.$emit('error', '图片加载失败')
      this.close()
    },
	
	// 将 Base64 转换为 Blob 对象
	base64ToBlob(base64Data) {
	    const parts = base64Data.split(';base64,');
	    const contentType = parts[0].split(':')[1];
	    const raw = window.atob(parts[1]);
	    const rawLength = raw.length;
	    const uInt8Array = new Uint8Array(rawLength);
	    for (let i = 0; i < rawLength; ++i) {
	        uInt8Array[i] = raw.charCodeAt(i);
	    }
	    return new Blob([uInt8Array], { type: contentType });
	},
	
	// 将 Blob 转换为临时文件 URL
	blobToTempUrl(blob) {
	    return URL.createObjectURL(blob);
	},
	
	// 上传 Base64 图片到云存储
	async uploadBase64ToCloudStorage(base64Data) {
		var that = this;
	    try {
	        // 将 Base64 转换为 Blob 对象
	        const blob = that.base64ToBlob(base64Data);
	        // 将 Blob 转换为临时文件 URL
	        const tempUrl = that.blobToTempUrl(blob);
	
	        // 生成云存储文件名
	        const cloudPath = `images/${new Date().getTime()}.png`;
	
	        // 上传临时文件到云存储
	        const res = await uniCloud.uploadFile({
	            cloudPath,
	            filePath: tempUrl
	        });
			uni.hideLoading()
			this.$emit('confirm', res)
	    } catch (error) {
			uni.hideLoading()
	        this.$emit('confirm', error)
	        throw error;
	    }
	}
	
	
  }
}
</script>
<style>
	uni-button{margin-right:30px;}
</style>
<style scoped>

/* 新增遮罩样式 */
.mask-overlay > view {
  position: absolute;
  background: rgba(0, 0, 0, 0.5);
}

.mask-top {
  top: 0;
  left: 0;
  right: 0;
}

.mask-bottom {
  bottom: 0;
  left: 0;
  right: 0;
}

.mask-left {
  left: 0;
}

.mask-right {
  right: 0;
}

/* 修改裁剪框样式 */
.crop-box {
  border: 2px solid #fff;
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.3);
}

/* 九宫格虚线样式 */
.grid-line {
  position: absolute;
  background: transparent;
  pointer-events: none;
}

.horizontal {
  left: 0;
  right: 0;
  height: 1px;
  border-bottom: 1px dashed rgba(255, 255, 255, 0.8);
}

.vertical {
  top: 0;
  bottom: 0;
  width: 1px;
  border-right: 1px dashed rgba(255, 255, 255, 0.8);
}

.line1.horizontal {
  top: 33.3%;
}

.line2.horizontal {
  top: 66.6%;
}

.line1.vertical {
  left: 33.3%;
}

.line2.vertical {
  left: 66.6%;
}

/* 调整调整手柄样式 */
.resize-handle {
  background: #fff;
  border: 2px solid #f95a58;
  width: 12px;
  height: 12px;
}



.cropper-wrapper {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 9999;
}

.cropper-mask {
  position: absolute;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.5);
}

.cropper-container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 500px;
  height: 680px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 0 20px rgba(0,0,0,0.2);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.cropper-header {
  height: 40px;
  background: #fff;
  color: white;
  display: flex;
  align-items: center;
  padding: 0 15px;
  cursor: move;
}

.title {
  flex: 1;
  font-size: 16px;
}

.close-btn {
  font-size: 24px;
  cursor: pointer;
  padding: 0 10px;
  color:#333;
}

.close-btn:hover {
  color: #666;
}

.cropper-content {
  flex: 1;
  position: relative;
  overflow: hidden;
}

.source-image {
  width: 100%;
  height: 100%;
}

.crop-box {
  position: absolute;
  border: 2px dashed #409EFF;
  cursor: move;
}

.resize-handle {
  position: absolute;
  width: 10px;
  height: 10px;
  background: #409EFF;
  border-radius: 50%;
}

.tr { top: -5px; right: -5px; cursor: ne-resize; }
.br { bottom: -5px; right: -5px; cursor: se-resize; }
.bl { bottom: -5px; left: -5px; cursor: sw-resize; }
.tl { top: -5px; left: -5px; cursor: nw-resize; }

.preview-area {
  height: 120px;
  border-top: 1px solid #eee;
  padding: 10px;
  display: flex;
  align-items: center;
}

.preview-title {
  width: 60px;
  font-size: 14px;
  color: #666;
}

.preview-canvas {
  width: 100px;
  height: 100px;
  border: 1px solid #ddd;
  margin: 0 20px;
}

.export-canvas {
  position: absolute;
  left: -9999px;
  width: 300px;
  height: 300px;
}

.confirm-btn {
  background: #f95a58;
  color: white;
  padding: 5px 35px;
  border-radius: 4px;
  font-size: 14px;
  border: none;
  cursor: pointer;
}

.confirm-btn:hover {
  background: #f95a58;
}
</style>